// package 客户端管理
package cli

import (
	cecdsa "crypto/ecdsa"
	"math/rand"
	"osiris/core/state"
	"osiris/core/txpool"
	"osiris/dao"
	"osiris/dto"
	"osiris/logger"
	"osiris/ocrypto/ecdsa"
	"osiris/p2p"
)

// VirtualClient 虚拟客户端，一个账户地址对应一个
type VirtualClient struct {
	Addr    []byte
	PrivKey *cecdsa.PrivateKey
	ChainID int
}

// DoRandomTx 随机发起交易：deposit, transform, pledge按一定概率比随机选一个
//
//	@receiver client
//	@return *dto.TxData
func (client *VirtualClient) DoRandomTx() *dto.TxData {
	accountLeaf, exist1 := state.GetAccountLeaf(client.Addr, client.ChainID)
	if !exist1 {
		logger.Error(map[string]interface{}{"[cli] [Do Random Tx] state.GetLeaf()": "account does not exist!"})
		return nil
	}

	//账户没余额时优先执行deposit交易
	if accountLeaf.Asset == 0 {
		return client.doDepositTx(accountLeaf)
	}

	//随机进行交易（amount和toAddr均随机）
	r := rand.Float32()
	coolDown := client.isSelfPeerCoolDown()
	if coolDown {
		switch {

		case r < 0.4:
			return client.doDepositTx(accountLeaf)

		case r < 0.8:
			return client.doTransferTx(accountLeaf)

		case r < 0.9:
			return client.doPledgeTx(accountLeaf)

		default:
			return nil
		}
	} else {
		switch {

		case r < 0.3:
			return client.doDepositTx(accountLeaf)

		case r < 0.55:
			return client.doTransferTx(accountLeaf)

		case r < 0.9:
			return client.doPledgeTx(accountLeaf)

		default:
			return nil
		}
	}
}

func (client *VirtualClient) doDepositTx(leaf dto.StateLeaf) *dto.TxData {
	addrStr := ecdsa.BytesToAccountStr(client.Addr)
	randomAmount := rand.Intn(10) + 1

	depositTxData := dto.TxData{
		TxType:   dto.TX_DEPOSIT,
		FromAddr: addrStr,
		ToAddr:   addrStr,
		Amount:   int64(randomAmount),
		Nonce:    client.getNonce(leaf),
	}
	depositTxData.HashAndSign(client.PrivKey, true)

	return &depositTxData
}

func (client *VirtualClient) doTransferTx(fromLeaf dto.StateLeaf) *dto.TxData {
	fromAddrStr := ecdsa.BytesToAccountStr(client.Addr)
	toAccountModel, err := dao.GetRandomAccount()
	if err != nil {
		logger.Error(map[string]interface{}{"[cli] [Do Transfer Tx] dao.GetRandomAccount()": err})
		return nil
	}

	if fromAddrStr == toAccountModel.Addr {
		return nil
	}

	randAmount := rand.Int63n(fromLeaf.Asset) + 1

	transferTx := dto.TxData{
		TxType:   dto.TX_TRANSFORM,
		FromAddr: fromAddrStr,
		ToAddr:   toAccountModel.Addr,
		Amount:   randAmount,
		Nonce:    client.getNonce(fromLeaf),
	}
	transferTx.HashAndSign(client.PrivKey, true)

	return &transferTx
}

func (client *VirtualClient) doPledgeTx(fromLeaf dto.StateLeaf) *dto.TxData {
	fromAddrStr := ecdsa.BytesToAccountStr(client.Addr)
	peerIDStr := p2p.GetSelfID().String()
	randAmount := rand.Int63n(fromLeaf.Asset) + 1

	pledgeTx := dto.TxData{
		TxType:   dto.TX_PLEDGE,
		FromAddr: fromAddrStr,
		ToAddr:   peerIDStr,
		Amount:   randAmount,
		Nonce:    client.getNonce(fromLeaf),
	}
	pledgeTx.HashAndSign(client.PrivKey, true)

	return &pledgeTx
}

func (client *VirtualClient) getNonce(leaf dto.StateLeaf) int64 {
	addrStr := ecdsa.BytesToAccountStr(client.Addr)
	return txpool.GetAcountNonceInPool(client.ChainID, addrStr)
}

func (client *VirtualClient) isSelfPeerCoolDown() bool {
	peerIDBytes, err := p2p.GetSelfID().MarshalBinary()
	if err != nil {
		logger.Error(map[string]interface{}{"[cli] [client.isSelfPeerCoolDown()] peer.ID.MarshalBinary()": err})
		return true
	}
	peerLeaf, exist2 := state.GetPeerLeaf(peerIDBytes, client.ChainID)
	if !exist2 {
		return true
	}
	if peerLeaf.CoolDown {
		return true
	}

	return false
}
